package com.xxxx.crm;

/**
 * Created by HMF on 2021/07/21 10:30
 */

import com.alibaba.fastjson.JSON;
import com.xxxx.crm.base.ResultInfo;
import com.xxxx.crm.exceptions.NoLoginException;
import com.xxxx.crm.exceptions.ParamsException;
import org.springframework.stereotype.Component;

import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 全局异常统一处理
 */
@Component   // 想要这个异常处理类生效，需要加上@Component注解，把它交给IOC处理，让IOC进行实例化
public class GlobalExceptionResolver implements HandlerExceptionResolver {

    /**
     * 异常处理的方法：
     * --controller层中的方法有两种返回值：
     * ----1.返回视图
     * ----2.返回数据（JSON数据）
     * <p>
     * --如何判断controller层中方法的返回值？
     * ----通过方法上是否声明@ResponseBody注解
     * ------如果未声明，则表示返回视图
     * ------如果声明了，则表示返回数据
     * ----然后通过反射来判断方法上有没有@ResponseBody注解
     *
     * @param request  request请求对象
     * @param response response响应对象
     * @param handler  方法对象
     * @param ex       异常对象
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        /**
         * 非法请求拦截：
         * -- 判断是否抛出未登录异常 NoLoginException
         * ---- 如果抛出该异常，则要求用户登录，重定向跳转到登录页面
         */
        if (ex instanceof NoLoginException) {
            // 重定向到登录页面
            ModelAndView mv = new ModelAndView("redirect:/index");
            return mv;
        }


        /**
         * 设置默认异常处理（返回视图）
         */
        ModelAndView modelAndView = new ModelAndView("error"); // 设置error页面视图，【注意项目里并没有提供error页面】
        // 设置在视图页面中展示的异常信息
        modelAndView.addObject("code", 500);
        modelAndView.addObject("msg", "系统异常，请重试....");


        // 判断 HandlerMethod，是否为方法 handler
        if (handler instanceof HandlerMethod) {

            // 类型转换为 HandlerMethod
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 通过反射，获取到方法上声明的@ResponseBody注解对象
            ResponseBody responseBody = handlerMethod.getMethod().getDeclaredAnnotation(ResponseBody.class); // 注意这个方法没有s

            // 判断ResponseBody对象是否为空（如果对象为空，则表示返回的是视图，如果不为空，则表示返回的是数据）
            if (responseBody == null) {
                /**
                 * 返回视图
                 */
                // 判断异常类型，看异常是不是自定义的参数异常 ParamsException
                if (ex instanceof ParamsException) {
                    ParamsException p = (ParamsException) ex;
                    // 设置异常信息，code和msg
                    modelAndView.addObject(p.getCode());
                    modelAndView.addObject(p.getMsg());
                }
                return modelAndView;

            } else {
                /**
                 * 返回数据
                 */
                // 设置默认的异常处理（返回数据）
                ResultInfo resultInfo = new ResultInfo();
                resultInfo.setCode(500);
                resultInfo.setMsg("系统异常，请重试！");

                // 判断异常类型是否是自定义的参数异常
                if (ex instanceof ParamsException) {
                    ParamsException p = (ParamsException) ex;
                    resultInfo.setCode(p.getCode());
                    resultInfo.setMsg(p.getMsg());
                }

                // 设置响应类型及编码格式（响应JSON格式的数据），防止中文乱码
                response.setContentType("application/json;charset=UTF-8");

                // 定义字符输出流
                PrintWriter out = null; // 提到try-catch外面，因为后面要使用

                try {
                    // 得到字符输出流
                    out = response.getWriter();

                    // 将需要返回的对象转换成JSON格式的字符，使用alibaba的fastjson转为json
                    String json = JSON.toJSONString(resultInfo);

                    // 输出数据
                    out.write(json);

                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    // 如果对象不为空，则关闭
                    if (out != null) {
                        out.close();
                    }
                }

                return null; // 因为这里已经使用流往外输出了，对于该方法就只能返回null了
            }
        }

        return modelAndView;
    }
}
